/**接口
介绍
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里，接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

接口初探
下面通过一个简单示例来观察接口是如何工作的： */

interface LabelledValue{
  label: string;
}
//类型检查器会查看printLabel的调用。 printLabel有一个参数，并要求这个对象参数有一个名为label类型为string的属性。
function printLable(lablelledObj: {label:string}){
  console.log(lablelledObj.label)
}
//LabelledValue接口就好比一个名字，用来描述上面例子里的要求。 它代表了有一个 label属性且类型为string的对象。
function printLable2(lablelledObj: LabelledValue){
  console.log(lablelledObj.label)
}

let myObj = {size: 10,label:"size 10 object"};
printLable(myObj)
printLable2(myObj)

/**可选属性
接口里的属性不全都是必需的。 有些是只在某些条件下存在，或者根本不存在。 可选属性在应用“option bags”模式时很常用，即给函数传入的参数对象中只有部分属性赋值了。
带有可选属性的接口与普通的接口定义差不多，只是在可选属性名字定义的后面加一个?符号。
 */
interface SquareConfig{
  color ? : string;
  width ? : number;
}

function createSquare(config:SquareConfig) : { color:string;area:number } {
   let newSquare = {color:'white',area:100};
   if(config.color){
    newSquare.color = config.color;
   }
   if(config.width){
    newSquare.area = config.width * config.width;
   }
   return newSquare
}

// let mySquare = createSquare({colorr:'red',width: 100})

/**只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性*/ 
interface Point {
  readonly x: number;
  readonly y: number;
}
let p1: Point = {x:10,y:20};
// p1.x=5 //readonly第一次赋值后，不让修改了

//TypeScript具有ReadonlyArray<T>类型，它与Array<T>相似，只是把所有可变方法去掉了，因此可以确保数组创建后再也不能被修改：
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
// ro[0]=12 //error


/**为了使用接口表示函数类型，我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。 */
interface SearchFunc{
  (source:string,subString,string) :boolean;
}
/**这样定义后，我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量，并将一个同类型的函数赋值给这个变量。 */
let mySearch:SearchFunc;
mySearch = function(source:string,subString:string){
  let result = source.search(subString);
  return result > -1;
}
//对于函数类型的类型检查来说，函数的参数名不需要与接口里定义的名字相匹配。 比如，我们使用下面的代码重写上面的例子：
mySearch = function(src:string,sub:string):boolean{
  let result = src.search(sub);
  return result > -1;
}

// 如果你不想指定类型，TypeScript的类型系统会推断出参数类型，因为函数直接赋值给了 SearchFunc类型变
mySearch = function(src,sub){
  let result = src.search(sub);
  return result > -1;
}

/**可索引的类型
与使用接口描述函数类型差不多，我们也可以描述那些能够“通过索引得到”的类型，比如a[10]或ageMap["daniel"]。 可索引类型具有一个 索引签名，它描述了对象索引的类型，还有相应的索引返回值类型。 让我们看一个例子：

例子里，我们定义了StringArray接口，它具有索引签名。 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。
*/
interface StringArray{
  [index:number]:string;  //索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。
}
let myArray: StringArray;
myArray=["Bob","Fred"];

let myStr : string = myArray[0]



class Amimal{
  name:string;
}
class fish{
  num: number;
}
class Dog extends Amimal{
  breed:string;
}
class LittltYellowCroaker extends fish{
  groupNum:number;
}
interface NotOkay{
  // [x : number] : Amimal;
  [x : string] : Dog;
}
interface FishOkay{
  //  [y: number]: fish;
  [y: number]: LittltYellowCroaker;
}


interface NumberDictionary{
  [index:string] : number;
  length: number;    // 可以，length是number类型
  //name: string;  // 错误，`name`的类型与索引类型返回值的类型不匹配
}


interface ReadonlyStringArray{
  readonly [index:number] :string;
}
let myArray1:ReadonlyStringArray = ["Alice","Bob"];
//myArray1[2]="111";//error  你不能设置myArray[2]，因为索引签名是只读的。


/**函数类型
 * 接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外，接口也可以描述函数类型。
 */
interface SearchFunc{
  (source:string,substring:string):boolean;
}
let mySearch2:SearchFunc;
//检查是否返回boolean类型
mySearch2 = function(source:string,subString:string){
  let result = source.search(subString);
  return result > -1;
}
let mysearch3: SearchFunc;
//boolean让类型让代码显示更为直接
mysearch3 = function(src:string,sub:string) : boolean{
  let r = src.search(sub);
  return r > -1;
}










/**
 * 类类型
 * 实现接口
与C#或Java里接口的基本作用一样，TypeScript也能够用它来明确的强制一个类去符合某种契约 */
interface ClockInterface{
  currentTime: Date;  //定义变量
  setTime(d: Date);   //定义方法
}

class Clock implements ClockInterface{
  currentTime: Date;
  constructor(h: number,m:number){}

  setTime(d: Date){
    this.currentTime = d;
  }
}

interface ClockConstructor{
  new(hour:number, minute:number): ClockInterface2;
}
// class Clock2 implements ClockConstructor{
//   currentTime:Date;
//   constructor(h:number,m:number){}
// }
interface ClockInterface2{
  tick();
}

function createClock(c:ClockConstructor,h:number,m:number): ClockInterface2{
  return new  c(h,m);
}
class DigitalClock implements ClockInterface2{
  constructor(h: number,m:number){}
  tick(){
    console.log("kk")
  }
}
class AnalogClock implements ClockInterface2 {
  constructor(h: number, m: number) { }
  tick() {
      console.log("tick tock");
  }
}

let digital = createClock(DigitalClock,12,17);
let analog = createClock(AnalogClock,13,16);



/**继承接口
和类一样，接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里，可以更灵活地将接口分割到可重用的模块里。*/ 
interface Shape{
  color:string;
}
interface Square extends Shape{
  sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;


interface PenStroke{
  penWidth: number;
}
interface Spen extends PenStroke,Shape{
  price:number;
}
let mySpen = <Spen>{};
mySpen.color="yellow";
mySpen.penWidth=10;
mySpen.price=20;


/**混合类型
先前我们提过，接口能够描述JavaScript里丰富的类型。 因为JavaScript其动态灵活的特点，有时你会希望一个对象可以同时具有上面提到的多种类型。
一个例子就是，一个对象可以同时做为函数和对象使用，并带有额外的属性。 */

interface Counter{
  (start:number): string;
  interval: number;
  reset(): void;
}
function getCounter(): Counter{
  let c = <Counter>function(start:number){};
  c.interval = 123;
  c.reset = function(){console.log("reset")};
  return c;
}
// let c = getCounter() 还会检测到 base.ts中定义的c,跨文件检查，范围比Java大
let mc = getCounter();
mc(10);
mc.reset();
mc.interval = 5.0;



/**接口继承类
当接口继承了一个类类型时，它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员，但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时，这个接口类型只能被这个类或其子类所实现（implement）。
当你有一个庞大的继承结构时这很有用，但要指出的是你的代码只在子类拥有特定属性时起作用。 这个子类除了继承至基类外与基类没有任何关系 */
class Control{
  private state: any;
}
//TypeScript的里面接口竟然可以继承类，和java不同
interface SelectableControl extends Control{
  select(): void;
}
class Button extends Control implements SelectableControl{
  select(): void {
      
  }
}
class TextBox extends Control{
  select(){}
}
//// 错误：“Image”类型缺少“state”属性。 要想实现SelectableControl 必须继承Control
// class Image implements SelectableControl{
//   select(): void {
//   }
// }


